// 本地时间(含日期, 其于std::time::SystemTime)
// 2023-0918
// by hzsong, in 2023-1022
// 未来需要支持：

use std::time::SystemTime;

use common_uu::IResult;

use crate::{a6_timestamp, a5_timezone::TimeZone};

use super::{b1_date::DateFT, b2_time::TimeFT};



/// local(or wall) datetime,
/// base on: std::time::SystemTime and fast date/time library.
/// for short: y=year, m=month, d=day, h=hour, m=minute, s=second, i=milli, o=micro
#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, Default, Debug)]
pub struct DateTimeFT {
    pub micros_since_unix_epoch: i64,
    pub timezone: TimeZone,
    pub date: DateFT,
    pub time: TimeFT,
}

impl DateTimeFT {
    pub fn now() -> Self {
        super::a6_timestamp::now().1
    }
    // pub fn now_chrono() -> Self {
    //         let now = Local::now();
    //         let date = DateFT::from_ymd_unsafe(now.year(), now.month() as u8, now.day() as u8);
    //         let time = now.time();
    // }
    pub fn from_std(time: SystemTime) -> Self {
        a6_timestamp::from_std(time)
    }
}
impl DateTimeFT {
    /// please maker sure: month∈[1, 12] && day∈[1, days_inmonth];
    /// in leap year: days_inmonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
    /// in non-leap year: days_inmonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    /// otherwise, an error will be throw.
    pub fn from_ymd(year: i32, month: u8, day: u8,) -> IResult<Self> {
        let date = DateFT::from_ymd(year, month, day)?;
        let (micros_since_unix_epoch, timezone) = a6_timestamp::get_timestamp_date_0_oclock(&date);

        Ok(Self {
            micros_since_unix_epoch,
            timezone,
            date,
            time: TimeFT::min(),
        })

    }
    
    /// please maker sure: month∈[1, 12] && day∈[1, days_inmonth];
    /// in leap year: days_inmonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
    /// in non-leap year: days_inmonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    /// otherwise, this function is unsafe.
    pub fn from_ymd_unsafe(year: i32, month: u8, day: u8,) -> Self {
        let date = DateFT::from_ymd_unsafe(year, month, day);
        let (micros_since_unix_epoch, timezone) = a6_timestamp::get_timestamp_date_0_oclock(&date);

        Self {
            micros_since_unix_epoch,
            timezone,
            date,
            time: TimeFT::min(),
        }
    }

    /// please maker sure: hour∈[0, 23] && minute∈[0, 59] && second∈[0, 59]; set io = 0(MicrosOneSec::min()) defaultly.
    /// otherwise, tan error will be throw.
    pub fn from_ymd_hms(year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> IResult<Self> {
        let date = DateFT::from_ymd(year, month, day)?;
        let time = TimeFT::from_3_hms(hour, minute, second)?;
        let (micros_date_0_oclock, timezone) = a6_timestamp::get_timestamp_date_0_oclock(&date);
        let micros_time = time.micros_of_day();
        Ok(Self {
            micros_since_unix_epoch: micros_date_0_oclock + micros_time,
            timezone,
            date,
            time,
        })

    }
    /// please maker sure: hour∈[0, 23] && minute∈[0, 59] && second∈[0, 59]; set io = 0(MicrosOneSec::min()) defaultly.
    /// otherwise, this function is unsafe.
    pub fn from_ymd_hms_unsafe(year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> Self {
        let date = DateFT::from_ymd_unsafe(year, month, day);
        let time = TimeFT::from_3_hms_unsafe(hour, minute, second);
        let (micros_date_0_oclock, timezone) = a6_timestamp::get_timestamp_date_0_oclock(&date);
        let micros_time = time.micros_of_day();
        Self {
            micros_since_unix_epoch: micros_date_0_oclock + micros_time,
            timezone,
            date,
            time,
        }

    }

    /// please maker sure: milli∈[0, 999] && micro∈[0, 999];
    /// otherwise, tan error will be thrown.
    pub fn from_ymd_hms_io(year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, milli: u16, micro: u16) -> IResult<Self> {
        let date = DateFT::from_ymd(year, month, day)?;
        let time = TimeFT::from_5_hmsio(hour, minute, second, milli, micro)?;
        let (micros_date_0_oclock, timezone) = a6_timestamp::get_timestamp_date_0_oclock(&date);
        let micros_time = time.micros_of_day();
        Ok(Self {
            micros_since_unix_epoch: micros_date_0_oclock + micros_time,
            timezone,
            date,
            time,
        })

    }
    /// please maker sure: milli∈[0, 999] && micro∈[0, 999];
    /// otherwise, tan error will be throw.
    pub fn from_ymd_hms_io_unsafe(year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, milli: u16, micro: u16) -> Self {
        let date = DateFT::from_ymd_unsafe(year, month, day);
        let time = TimeFT::from_5_hmsio_unsafe(hour, minute, second, milli, micro);
        let (micros_date_0_oclock, timezone) = a6_timestamp::get_timestamp_date_0_oclock(&date);
        let micros_time = time.micros_of_day();
        Self {
            micros_since_unix_epoch: micros_date_0_oclock + micros_time,
            timezone,
            date,
            time,
        }

    }


}



impl DateTimeFT {
    /// example: yyyymmdd=2023_10_24_u32
    /// 2023-10-24T00:00:00(000)000
    pub fn from_ymd_friendly_unsafe(yyyymmdd: u32) -> Self {
        let year = yyyymmdd / 1_00_00;
        let month_day = yyyymmdd % 1_00_00;
        let month = month_day / 100;
        let day = month_day % 100;

        Self::from_ymd_unsafe(year as i32, month as u8, day as u8)
    }
    /// example: yyyymmdd=2023_10_24_u32, hhmmss=12_34_56_u32;
    /// 2023-10-24T12:34:56(000)000
    pub fn from_ymd_hms_friendly_unsafe(yyyymmdd: u32, hhmmss: u32) -> Self {
        let year = yyyymmdd / 1_00_00;
        let month_day = yyyymmdd % 1_00_00;
        let month = month_day / 100;
        let day = month_day % 100;

        let hour = hhmmss / 1_00_00 % 24;
        let minute_second = hhmmss % 1_00_00;
        let minute = minute_second / 100;
        let second = minute_second % 100;

        Self::from_ymd_hms_unsafe(year as i32, month as u8, day as u8, hour as u8, minute as u8, second as u8)
    }
    
    /// example: yyyymmdd=2023_10_24_u32, hhmmss=12_34_56_u32; iiiooo=708_901_u32
    /// 2023-10-24T12:34:56(708)901
    pub fn from_ymd_hms_io_friendly_unsafe(yyyymmdd: u32, hhmmss: u32, iiiooo: u32) -> Self {
        let year = yyyymmdd / 1_00_00;
        let month_day = yyyymmdd % 1_00_00;
        let month = month_day / 100;
        let day = month_day % 100;

        let hour = hhmmss / 1_00_00 % 24;
        let minute_second = hhmmss % 1_00_00;
        let minute = minute_second / 100;
        let second = minute_second % 100;

        let milli = iiiooo / 1000 % 1000;
        let micro = iiiooo % 1000;

        Self::from_ymd_hms_io_unsafe(year as i32, month as u8, day as u8, hour as u8, minute as u8, second as u8, milli as u16, micro as u16)
    }
    
    /// example: yyyymmdd=20231024_123456_u64
    /// 2023-10-24T12:34:56(000)000
    pub fn from_14_ymdhms_friendly_unsafe(yyyymmdd_hhmmss: u64) -> Self {
        const have_10_zero: u64 = 1_00_00_000000;
        let year = yyyymmdd_hhmmss / have_10_zero;
        let mmdd_hhmmss = yyyymmdd_hhmmss % have_10_zero;
        let year = if year > i32::MAX as u64 {
            i32::MAX
        } else {
            year as i32
        };
        // =========== same codes
        const have_08_zero: u64 = 1_00_000000;
        let month = mmdd_hhmmss / have_08_zero;
        let dd_hhmmss = (mmdd_hhmmss % have_08_zero) as u32;
        
        const have_06_zero: u32 = 1_000000;
        let day = dd_hhmmss / have_06_zero;
        let hhmmss = dd_hhmmss % have_06_zero;

        const have_04_zero: u32 = 1_0000;
        let hour = hhmmss / have_04_zero % 24;
        let mmss = hhmmss % have_04_zero;

        const have_02_zero: u32 = 1_00;
        let minute = mmss / have_02_zero;
        let second = mmss % have_02_zero;
        // =========== same codes

        Self::from_ymd_hms_unsafe(year, month as u8, day as u8, hour as u8, minute as u8, second as u8)
    }

    /// example: yyyymmdd_hhmmss_iii=20231024_123456_708_u64
    /// 2023-10-24T12:34:56(708)000
    pub fn from_17_ymdhmsi_friendly_unsafe(yyyymmdd_hhmmss_iii: u64) -> Self {
        const have_13_zero: u64 = 1_00_00_000000_000;
        let year = yyyymmdd_hhmmss_iii / have_13_zero;
        let mmdd_hhmmss_iii = yyyymmdd_hhmmss_iii % have_13_zero;
        let year = if year > i32::MAX as u64 {
            i32::MAX
        } else {
            year as i32
        };

        const have_03_zero: u64 = 1_000;
        let milli = mmdd_hhmmss_iii % have_03_zero;
        let mmdd_hhmmss = mmdd_hhmmss_iii / have_03_zero;

        

        // =========== same codes
        const have_08_zero: u64 = 1_00_000000;
        let month = mmdd_hhmmss / have_08_zero;
        let dd_hhmmss = (mmdd_hhmmss % have_08_zero) as u32;
        
        const have_06_zero: u32 = 1_000000;
        let day = dd_hhmmss / have_06_zero;
        let hhmmss = dd_hhmmss % have_06_zero;

        const have_04_zero: u32 = 1_0000;
        let hour = hhmmss / have_04_zero % 24;
        let mmss = hhmmss % have_04_zero;

        const have_02_zero: u32 = 1_00;
        let minute = mmss / have_02_zero;
        let second = mmss % have_02_zero;
        // =========== same codes

        Self::from_ymd_hms_io_unsafe(year as i32, month as u8, day as u8, hour as u8, minute as u8, second as u8, milli as u16, 0)
    }

    /// example: yyyymmdd_hhmmss=20231024_123456_u64, iiiooo=708901_u32
    /// 2023-10-24T12:34:56(708)901
    /// u64::MAX = 1844_6744_0737_0955_1615, i64::MIN = 922_3372_0368_5477_5807: only support 19 digitals.
    pub fn from_20_ymdhmsio_friendly_unsafe(yyyymmdd_hhmmss: u64, iiiooo: u32) -> Self {
        const have_10_zero: u64 = 1_00_00_000000;
        let year = yyyymmdd_hhmmss / have_10_zero;
        let mmdd_hhmmss = yyyymmdd_hhmmss % have_10_zero;
        let year = if year > i32::MAX as u64 {
            i32::MAX
        } else {
            year as i32
        };
        // =========== same codes
        const have_08_zero: u64 = 1_00_000000;
        let month = mmdd_hhmmss / have_08_zero;
        let dd_hhmmss = (mmdd_hhmmss % have_08_zero) as u32;
        
        const have_06_zero: u32 = 1_000000;
        let day = dd_hhmmss / have_06_zero;
        let hhmmss = dd_hhmmss % have_06_zero;

        const have_04_zero: u32 = 1_0000;
        let hour = hhmmss / have_04_zero % 24;
        let mmss = hhmmss % have_04_zero;

        const have_02_zero: u32 = 1_00;
        let minute = mmss / have_02_zero;
        let second = mmss % have_02_zero;
        // =========== same codes

        
        const have_03_zero: u32 = 1_000;
        let milli = iiiooo  / have_03_zero;
        let micro = iiiooo  % have_03_zero;

        Self::from_ymd_hms_io_unsafe(year, month as u8, day as u8, hour as u8, minute as u8, second as u8, milli as u16, micro as u16)
    }

}



impl DateTimeFT {
    pub fn to_print(&self) -> String {
        format!("{}T{}", self.date.to_print_10(), self.time.to_print_hmsio(),)
    }
    pub fn to_print_timezone(&self) -> String {
        format!("{}T{},{}", self.date.to_print_10(), self.time.to_print_hmsio(), self.timezone)
    }
    pub fn to_print_hmsio(&self) -> String {
        self.time.to_print_hmsio()
    }
    pub fn to_print_hmsi(&self) -> String {
        self.time.to_print_hmsi()
    }

    pub fn ellapse_string(&self, tm_before: &Self) -> String {
        let micros = self.micros_since_unix_epoch - tm_before.micros_since_unix_epoch;

        let mut secs = micros / super::a8_micros::MICROS_PER_SEC as i64;
        let mut micros = (micros % super::a8_micros::MICROS_PER_SEC as i64) as i32;
        if micros < 0 {
            secs -= 1;
            micros += super::a8_micros::MICROS_PER_SEC;
        }
        let milli = micros / super::a8_micros::MICROS_PER_MILLIS;
        let micro = micros % super::a8_micros::MICROS_PER_MILLIS;
        format!("{secs:0>2}({milli:0>3}){micro:0>3}")
    }

    pub fn millis_since_unix_epoch(&self) -> i64 {
        self.micros_since_unix_epoch / 1000
    }

    pub fn micros_since_unix_epoch(&self) -> i64 {
        self.micros_since_unix_epoch
    }

    pub fn micro_from(&self, tm_before: &Self) -> i64 {
        self.micros_since_unix_epoch - tm_before.micros_since_unix_epoch
    }

    pub fn millis_from(&self, tm_before: &Self) -> i64 {
        (self.micros_since_unix_epoch - tm_before.micros_since_unix_epoch) / 1000
    }


}



impl DateTimeFT {
    /// 14位: 日期8位 + 时间6位
    pub fn ymd_hms_friendly(&self) -> i64 {
        self.date.ymd_friendly() as i64 * 1_000000_i64 + self.time.hms_friendly() as i64
    }
    /// 17位: 日期8位 + 时间9位
    pub fn ymd_hmsi_friendly(&self) -> i64 {
        self.date.ymd_friendly() as i64 * 1_000000_000_i64 + self.time.hmsi_friendly() as i64
    }
}

impl std::fmt::Display for DateTimeFT {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.to_print())
    }
}


// impl std::fmt::Display for DateTimeFT {
//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//         write!(f, "{}", self.date.to_print_10(), self.time.to_print_hmsio())
//     }
// }